using System.Collections.Generic;
using UnityEngine;

namespace ServerTools
{
    public class Triangle
    {
        public Vector3 A;
        public Vector3 B;
        public Vector3 C;
        public Triangle neighborA = null;
        public Triangle neighborB = null;
        public Triangle neighborC = null;
        public int id;
        public Triangle father = null;
        public Triangle next = null;
        public float F = 0f;
        public float H = 0f;
        public float G = 0f;
        public bool finished = false;
        public void reset()
        {
            F = 0f;
            H = 0f;
            G = 0f;
            neighborA = null;
            neighborB = null;
            neighborC = null;
            father = null;
            next = null;
            finished = false;
        }
        public Vector3 getCenterPosition()
        {
            float a = (B - C).magnitude;
            float b = (B - C).magnitude;
            float c = (B - C).magnitude;

            Vector3 Center = new Vector3((a * A.x + b * B.x + c * C.x) / (a + b + c),
                (a * A.y + b * B.y + c * C.y) / (a + b + c),
                (a * A.z + b * B.z + c * C.z) / (a + b + c));
            return Center;
        }
    }
    
    public class ServerPath
    {
        private float countDis(Triangle TA, Triangle TB)
        {
            Vector3 ACenter = TA.getCenterPosition();
            Vector3 BCenter = TB.getCenterPosition();
            return (ACenter - BCenter).magnitude;
        }
        private ServerPath()
        {

        }
        private static ServerPath instance = null;
        public static ServerPath getInstance()
        {
            if(instance == null)
            {
                instance = new ServerPath();
            }
            return instance;
        }
        public List<Triangle> listDraw = new List<Triangle>();
        private List<Triangle> listcheck = new List<Triangle>();
        private List<Triangle> listfinish = new List<Triangle>();
        public void initNeighbour()
        {
            for (int i = 0; i < listDraw.Count; ++i)
            {
                listDraw[i].reset();
            }
            for (int i = 0; i < listDraw.Count; ++i)
            {
                for (int j = 0; j < listDraw.Count; ++j)
                {
                    if(i == j)
                    {
                        continue;
                    }
                    int bingleCount = 0;
                    bool hitA = false;
                    bool hitB = false;
                    bool hitC = false;
                    if ((listDraw[i].A - listDraw[j].A).magnitude < 0.05000 ||
                        (listDraw[i].A - listDraw[j].B).magnitude < 0.05000 ||
                        (listDraw[i].A - listDraw[j].C).magnitude < 0.05000)
                    {
                        ++bingleCount;
                        hitA = true;
                    }
                    if ((listDraw[i].B - listDraw[j].A).magnitude < 0.05000 ||
                        (listDraw[i].B - listDraw[j].B).magnitude < 0.05000 ||
                        (listDraw[i].B - listDraw[j].C).magnitude < 0.05000)
                    {
                        ++bingleCount;
                        hitB = true;
                    }
                    if ((listDraw[i].C - listDraw[j].A).magnitude < 0.05000 ||
                        (listDraw[i].C - listDraw[j].B).magnitude < 0.05000 ||
                        (listDraw[i].C - listDraw[j].C).magnitude < 0.05000)
                    {
                        ++bingleCount;
                        hitC = true;
                    }
                    if(bingleCount > 1)
                    {
                        tryToAddNeighbour(listDraw[i], listDraw[j], hitA, hitB, hitC);
                    }
                }
            }
        }
        public void countPath(int start, int end)
        {
            if(start >= listDraw.Count || end >= listDraw.Count)
            {
                return;
            }
            listcheck.Clear();
            listfinish.Clear();
            for(int i = 0; i < listDraw.Count; ++i)
            {
                listDraw[i].id = i;
            }
            Triangle nowTriangle;
            tryToAddToListcheck(listDraw[start]);
            int ci = 0;
            while((nowTriangle = getPbByMinF()) != null)
            {
                ++ci;
                //Debug.Log(ci);
                if(nowTriangle.neighborA == listDraw[end] ||
                    nowTriangle.neighborB == listDraw[end] ||
                    nowTriangle.neighborC == listDraw[end])
                {
                    Triangle pbend = listDraw[end];
                    listfinish.Add(pbend);
                    pbend.father = nowTriangle;
                    while(pbend.father != null)
                    {
                        Debug.DrawLine(pbend.getCenterPosition(), pbend.father.getCenterPosition());
                        pbend.father.next = pbend;
                        pbend = pbend.father;
                    }
                    optimizationPath(pbend);
                    break;
                }
                if(countNeighbourF(nowTriangle, nowTriangle.neighborA))
                {
                    nowTriangle.neighborA.H = (nowTriangle.neighborA.getCenterPosition() - listDraw[end].getCenterPosition()).magnitude;
                    nowTriangle.neighborA.F = nowTriangle.neighborA.H + nowTriangle.neighborA.G;
                }
                if(countNeighbourF(nowTriangle, nowTriangle.neighborB))
                {
                    nowTriangle.neighborB.H = (nowTriangle.neighborB.getCenterPosition() - listDraw[end].getCenterPosition()).magnitude;
                    nowTriangle.neighborB.F = nowTriangle.neighborB.H + nowTriangle.neighborB.G;
                }
                if(countNeighbourF(nowTriangle, nowTriangle.neighborC))
                {
                    nowTriangle.neighborC.H = (nowTriangle.neighborC.getCenterPosition() - listDraw[end].getCenterPosition()).magnitude;
                    nowTriangle.neighborC.F = nowTriangle.neighborC.H + nowTriangle.neighborC.G;
                }
                addToFinish(nowTriangle);
            }
        }
        private Triangle getPbByMinF()
        {
            Triangle pb = null;
            for (int i = 0; i < listcheck.Count; ++i)
            {
                if(pb == null || pb.F > listcheck[i].F)
                {
                    pb = listcheck[i];
                }
            }
            return pb;
        }
        private void tryToAddToListcheck(Triangle t)
        {
            for (int i = 0; i < listfinish.Count; ++i)
            {
                if (t.id == listfinish[i].id)
                {
                    return;
                }
            }
            for (int i = 0; i < listcheck.Count; ++i)
            {
                if(t.id == listcheck[i].id)
                {
                    return;
                }
            }
            listcheck.Add(t);
        }
        private void addToFinish(Triangle t)
        {
            t.finished = true;
            listfinish.Add(t);
            listcheck.Remove(t);
        }
        private bool countNeighbourF(Triangle A, Triangle Neighbour)
        {
            if(Neighbour == null || Neighbour.finished)
            {
                return false;
            }
            float G = countDis(A, Neighbour);
            if (Neighbour.G == 0f || (A.G + G) < Neighbour.G)
            {
                Neighbour.G = A.G + G;
                Neighbour.father = A;
                tryToAddToListcheck(Neighbour);
                return true;
            }
            return false;
        }
        private void tryToAddNeighbour(Triangle baseTriangle, Triangle neighbour, bool hitA, bool hitB, bool hitC)
        {
            if(hitA && hitB)
            {
                baseTriangle.neighborC = neighbour;
            }
            else if (hitA && hitC)
            {
                baseTriangle.neighborB = neighbour;
            }
            else if (hitC && hitB)
            {
                baseTriangle.neighborA = neighbour;
            }
        }
        private void optimizationPath(Triangle start)
        {
            Vector3 v1 = new Vector3();
            Vector3 v2 = new Vector3();
            Vector3 v3 = new Vector3();
            Vector3 v4 = new Vector3();

            Vector3 p1 = new Vector3();
            Vector3 p2 = new Vector3();
            Vector3 p3 = new Vector3();
            Vector3 p4 = new Vector3();
            Triangle g1 = start;
            Triangle g2 = start;
            List<Vector3> listpath = new List<Vector3>();
            Vector3 startPosition = start.getCenterPosition();
            listpath.Add(startPosition);
            Triangle tCount = start.next;
            caculateV(startPosition, start, ref v1, ref v2, ref p1, ref p2);
            int testi = 0;
            while (tCount != null)
            {
                ++testi;
                caculateV(startPosition, tCount, ref v3, ref v4, ref p3, ref p4);

                if (testi == 999)
                {
                    Debug.DrawRay(startPosition + Vector3.up * 0.5000f, v1, Color.cyan, 5);
                    Debug.DrawRay(startPosition + Vector3.up * 0.5000f, v2, Color.cyan, 5);
                    Debug.DrawRay(startPosition, v3, Color.black, 5);
                    Debug.DrawRay(startPosition, v4, Color.black, 5);
                    tCount = null;
                    continue;
                }


                //左手坐标系，所以小于等于
                if (Vector3.Cross(new Vector3(v3.x, 0, v3.z), new Vector3(v2.x, 0, v2.z)).y <= 0)
                {
                    Debug.DrawRay(p2, Vector3.up * 10, Color.yellow, 1);
                    startPosition = p2;
                    tCount = g2;
                    g2 = tCount.next;
                    caculateV(startPosition, tCount, ref v1, ref v2, ref p1, ref p2);
                    listpath.Add(startPosition);
                    this.countNext(ref listpath, ref tCount);
                    continue;
                }
                //左手坐标系，所以大于等于
                if (Vector3.Cross(new Vector3(v4.x, 0, v4.z), new Vector3(v1.x, 0, v1.z)).y >= 0)
                {
                    Debug.DrawRay(p1, Vector3.up * 10, Color.yellow, 1);
                    startPosition = p1;
                    tCount = g1;
                    g1 = tCount.next;
                    caculateV(startPosition, tCount, ref v1, ref v2, ref p1, ref p2);
                    listpath.Add(startPosition);
                    this.countNext(ref listpath, ref tCount);
                    continue;
                }
                //左手坐标系，所以小于等于
                if (Vector3.Cross(new Vector3(v3.x, 0, v3.z), new Vector3(v1.x, 0, v1.z)).y <= 0)
                {
                    v1 = v3;
                    p1 = p3;
                    g1 = tCount.next;
                }
                //左手坐标系，所以小于等于
                if (Vector3.Cross(new Vector3(v4.x, 0, v4.z), new Vector3(v2.x, 0, v2.z)).y >= 0)
                {
                    v2 = v4;
                    p2 = p4;
                    g2 = tCount.next;
                }
                this.countNext(ref listpath, ref tCount);
            }
            for(int i = 0; i < listpath.Count - 1; ++i)
            {
                Debug.DrawLine(listpath[i], listpath[i + 1], Color.blue, 5);
            }
        }
        private void countNext(ref List<Vector3> listpath, ref Triangle t)
        {
            if (t.next == null)
            {
                listpath.Add(t.getCenterPosition());
            }
            t = t.next;
        }
        private void caculateV(Vector3 startPosition, Triangle tCount, ref Vector3 v1, ref Vector3 v2, ref Vector3 p1, ref Vector3 p2)
        {
            if(tCount == null)
            {
                return;
            }
            if(tCount.next != null)
            {
                if (tCount.next == tCount.neighborA)
                {
                    v1 = tCount.B - startPosition;
                    v2 = tCount.C - startPosition;
                    p1 = tCount.B;
                    p2 = tCount.C;
                }
                else if (tCount.next == tCount.neighborB)
                {
                    v1 = tCount.A - startPosition;
                    v2 = tCount.C - startPosition;
                    p1 = tCount.A;
                    p2 = tCount.C;
                }
                else if (tCount.next == tCount.neighborC)
                {
                    v1 = tCount.B - startPosition;
                    v2 = tCount.A - startPosition;
                    p1 = tCount.B;
                    p2 = tCount.A;
                }
                //start Position和另一个V，在同一个边上则边就确定了，从这个三角形中心找左右
                if (Vector3.Cross(new Vector3(v1.x, 0, v1.z), new Vector3(v2.x, 0, v2.z)).y == 0)
                {
                    Vector3 vcp_1 = p1 - tCount.getCenterPosition();
                    Vector3 vcp_2 = p2 - tCount.getCenterPosition();

                    if (Vector3.Cross(new Vector3(vcp_1.x, 0, vcp_1.z), new Vector3(vcp_2.x, 0, vcp_2.z)).y < 0)
                    {
                        v1 = p2 - startPosition;
                        v2 = p1 - startPosition;
                        Vector3 pswitch = p1;
                        p1 = p2;
                        p2 = pswitch;
                    }
                    else
                    {
                        v1 = p1 - startPosition;
                        v2 = p2 - startPosition;
                    }
                }
                //左手坐标系，所以小于等于
                else if (Vector3.Cross(new Vector3(v1.x, 0, v1.z), new Vector3(v2.x, 0, v2.z)).y < 0)
                {
                    Vector3 vswitch = v1;
                    Vector3 pswitch = p1;
                    v1 = v2;
                    v2 = vswitch;
                    p1 = p2;
                    p2 = pswitch;
                }
            }
            else
            {
                v1 = v2 = tCount.getCenterPosition() - startPosition;
                p1 = p2 = tCount.getCenterPosition();
            }
        }
    }

}
